import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;

import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.PrettyXmlSerializer;
import org.htmlcleaner.TagNode;

public class ProfessorListPage {
	private String url;
	private TagNode tagNode;
	private boolean hasMorePages = false;

	/*		This class represents a parsing mechanism for the page found at 
	 * 
	 * 		info.iastate.edu and for various sub searches at that page
	 * 
	 * 		this class is used to parse this webpage for data
	 * 
	 * 		Note that is class is specifically used only for info.iastate.edu
	 */
	
	
	/*
	 * This constructor takes a string as url, loads the page and then parses it into html
	 */
	public ProfessorListPage(String url) throws MalformedURLException,
			IOException {
		
		this.url = url;
		
		//define html cleaner properties
		CleanerProperties props = new CleanerProperties();
		props.setTranslateSpecialEntities(true);
		props.setTransResCharsToNCR(true);
		props.setOmitComments(true);

		//loads page and parses into html
		tagNode = new HtmlCleaner(props).clean(new URL(url));
		
		//saves html and xml file
		new PrettyXmlSerializer(props).writeToFile(tagNode, "testBanana2.xml",
				"utf-8");
	}

	/*
	 * Method extracts the names of all the departments
	 * 
	 * Note: only works with info.iastate.edu/advanced and not with any other
	 */
	public ArrayList<String> gatherDepartmentNames() {

		ArrayList<String> departmentNames = new ArrayList<String>();

		TagNode formtable = findTagNodeForName(tagNode, "table", "class",
				"form_table");

		TagNode tbody = getChildByNumber(formtable, 0);

		TagNode tr4 = getChildByNumber(tbody, 3);

		TagNode td = getChildByNumber(tr4, 1);

		TagNode select = getChildByNumber(td, 1);

		TagNode[] departmentNodes = select.getChildTags();

		for (TagNode node : departmentNodes) {
			departmentNames.add(node.getText().toString());

		}
		//removes the first entry because it is not a department
		departmentNames.remove(0);
		return departmentNames;
	}

	/*
	 * This link finds the other links in the page
	 * This is important for departments with more than 100 members
	 * 
	 */
	public ArrayList<String> gatherOtherLinks() {

		ArrayList<String> otherLinks = new ArrayList<String>();

		TagNode info = findTagNodeForName(tagNode, "table", "id", "ind_results");

	
		TagNode body = getChildByNumber(info, 0);

		TagNode firstrow = getChildByNumber(body, 0);

		TagNode th = getChildByNumber(firstrow, 0);
		TagNode ul = getChildByNumber(th, 0);

		TagNode[] linkNodes = ul.getChildTags();
		boolean foundCurrent = false;
		for (TagNode node : linkNodes) {
			String url = findLinkInSubNode(node);
			if (foundCurrent && url != null) {
				otherLinks.add(url);
			}
			if(url == null){
				foundCurrent = true;
			}

		}

		return otherLinks;
	}
	
	/*
	 * Method to be run after gather professor links has been run.
	 * 
	 * gather professor links sets this method to be true if it has run
	 */
	public boolean hasMorePages() {
		return hasMorePages;
	}

	public ArrayList<String> gatherProfessorLinks() throws NoProfessorException {

		ArrayList<String> urls = new ArrayList<String>();

		TagNode info = findTagNodeForName(tagNode, "table", "id", "ind_results");

		if(info == null){
			throw new NoProfessorException("No Professors in " + url);
		}
		
		TagNode body = getChildByNumber(info, 0);

		TagNode[] infoNodes = body.getChildTags();

		int nodesWithoutInfo = 0;

		for (TagNode node : infoNodes) {
			if (node.hasAttribute("class")) {
				String url = findLinkInSubNode(node);
				System.out.println(": " + url);
				urls.add(url);
			} else {
				nodesWithoutInfo++;
			}
		}

		if (nodesWithoutInfo > 1) {
			hasMorePages = true;
		}

		return urls;
	}
	
	/*
	 * Private helper method, searches through subnodes of "node" for a node with a certain name and attribute
	 */
	private static TagNode findTagNodeForName(TagNode node, String name,
			String attribute_name, String attribute_value) {
		if (node.getName() != null && node.getName().equals(name)) {
			Map<String, String> attributes = node.getAttributes();
			Set<String> keySet = attributes.keySet();

			for (String key : keySet) {
				if (key.equals(attribute_name)
						&& attributes.get(key).equals(attribute_value)) {
					return node;
				}
			}
		}

		TagNode[] tags = node.getChildTags();
		for (TagNode tag : tags) {
			TagNode newTag = findTagNodeForName(tag, name, attribute_name,
					attribute_value);
			if (newTag != null) {
				return newTag;
			}
		}
		return null;

	}
	/*
	 * returns a number of a node for a child
	 */
	public static TagNode getChildByNumber(TagNode parent, int number) {
		TagNode[] childTags = parent.getChildTags();
		return childTags[number];

	}
	
	/**
	 * searches a subnode of node for any link and returns its value
	 * @param node node to search from
	 * @return the value of the link
	 */
	public static String findLinkInSubNode(TagNode node) {
		if (node.getName().equals("a")) {
			Map<String, String> attributes = node.getAttributes();
			return attributes.get("href");
		}

		TagNode[] tags = node.getChildTags();
		for (TagNode tag : tags) {
			String newTag = findLinkInSubNode(tag);
			if (newTag != null) {
				return newTag;
			}
		}
		return null;
	}

}
